﻿using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.EntityFrameworkCore;
using WorkFlowCore.Authorization;
using WorkFlowCore.Framework.MultipleApp;
using WorkFlowCore.IRepositories;
using WorkFlowCore.UserSelectors;
using WorkFlowCore.WorkTasks;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;

namespace WorkFlowCore.Framework.Repositories4EF
{
    public class WorkTaskRepository : BasicRepository<WorkTaskInfo, Guid>, IWorkTaskRepository
    {
        private readonly IWorkflowSession session;
        private readonly WorkflowDbContext workflowDbContext;
        private readonly IWorkStepRepository workStepRepository;
        private readonly IAppContextManager appContextManager;

        public WorkTaskRepository(IWorkflowSession session, 
            WorkflowDbContext workflowDbContext, 
            IDbContextProvider<WorkflowDbContext> dbContextProvider, 
            IWorkStepRepository workStepRepository, 
            IAppContextManager appContextManager)
            : base(session, dbContextProvider, appContextManager)
        {
            this.session = session;
            this.workflowDbContext = workflowDbContext;
            this.workStepRepository = workStepRepository;
            this.appContextManager = appContextManager;
        }

        public async Task<PageResult<StepWorkTask>> GetAllTasksOfUserAsync(string userId, int pageIndex = 1, int pageSize = -1)
        {
            var stepPredicate = new Func<WorkStepInfo, bool>(ws=>ws.HandleUser_Id == userId);
            var workStepInfos = workflowDbContext.Set<WorkStepInfo>().AsNoTracking().WhereMultipleApp(appContextManager.GetAppId()).Where(ws => ws.HandleUser_Id == userId);
            var workTaskInfos = workflowDbContext.Set<WorkTaskInfo>().AsNoTracking().WhereMultipleApp(appContextManager.GetAppId()).Where(wt => !wt.IsSimulation);

            return await GetResultAsync(workStepInfos, ws=>ws.HandleUser_Id == userId, workTaskInfos, pageIndex, pageSize);
        }

        public async Task<PageResult<StepWorkTask>> GetHandledWorkTasksOfUserAsync(string userId, int pageIndex = 1, int pageSize = -1)
        {
            var workStepInfos = workflowDbContext.Set<WorkStepInfo>().AsNoTracking().WhereMultipleApp(appContextManager.GetAppId()).Where(ws => ws.HandleUser_Id == userId && (ws.WorkStepType == WorkStepType.ReadOnly || ws.IsHandled && ws.HandleType != WorkStepHandleType.UnWork));
            var workTaskInfos = workflowDbContext.Set<WorkTaskInfo>().AsNoTracking().WhereMultipleApp(appContextManager.GetAppId()).Where(wt => !wt.IsSimulation && wt.CreatedUserId != userId);
            return await GetResultAsync(workStepInfos, ws => ws.HandleUser_Id == userId && (ws.WorkStepType == WorkStepType.ReadOnly || ws.IsHandled && ws.HandleType != WorkStepHandleType.UnWork), workTaskInfos, pageIndex, pageSize);
        }

        public async Task<PageResult<WorkTask>> GetTasksOfStartUserAsync(string userId, int pageIndex = 1, int pageSize = -1)
        {
            var result = new PageResult<WorkTask>
            {
                Total = (await GetCountAsync(wt => wt.ModifiedUserId == userId))
            };
            var worktaskQuery = workflowDbContext.Set<WorkTaskInfo>().AsNoTracking().WhereMultipleApp(appContextManager.GetAppId()).Where(wt => wt.ModifiedUserId == userId);

            if (pageSize < 1)
                result.Items = worktaskQuery.Select(ws => ws.ToWorkTask()).ToList();
            else result.Items = worktaskQuery.OrderByDescending(w => w.CreationTime).Skip((pageIndex - 1) * pageSize).Take(pageSize).Select(w => w.ToWorkTask()).ToList();
            return await Task.FromResult(result);
        }

        public async Task<PageResult<StepWorkTask>> GetUnHandledWorkTasksOfUserAsync(string userId, int pageIndex = 1, int pageSize = -1)
        {
            var workStepInfos = workflowDbContext.Set<WorkStepInfo>().AsNoTracking().WhereMultipleApp(appContextManager.GetAppId()).Where(ws => ws.HandleUser_Id == userId && !ws.IsHandled);
            var workTaskInfos = workflowDbContext.Set<WorkTaskInfo>().AsNoTracking().WhereMultipleApp(appContextManager.GetAppId()).Where(wt => !wt.IsSimulation);

            return await GetResultAsync(workStepInfos, ws => ws.HandleUser_Id == userId && !ws.IsHandled, workTaskInfos, pageIndex, pageSize);
        }

        public async Task<PageResult<WorkTask>> GetWorkTasksOfCreatorAsync(string userId, int pageIndex = 1, int pageSize = -1)
        {
            var worktaskQuery = workflowDbContext.Set<WorkTaskInfo>().AsNoTracking().WhereMultipleApp(appContextManager.GetAppId()).Where(wt => !wt.Deleted && wt.CreatedUserId == userId && !wt.IsSimulation);
            var result = new PageResult<WorkTask>
            {
                Total = worktaskQuery.Count()
            };
            if (pageSize < 1)
                result.Items = worktaskQuery.Select(w => w.ToWorkTask()).ToList();
            else result.Items = worktaskQuery.OrderByDescending(w => w.CreationTime).Skip((pageIndex - 1) * pageSize).Take(pageSize).Select(w => w.ToWorkTask()).ToList();
            return await Task.FromResult(result);
        }

        public async Task<PageResult<WorkTask>> GetAllWorkTasksAsync(int pageIndex = 1, int pageSize = -1)
        {
            var worktaskQuery = workflowDbContext.Set<WorkTaskInfo>().AsNoTracking().WhereMultipleApp(appContextManager.GetAppId()).Where(wt => !wt.Deleted && !wt.IsSimulation);
            var result = new PageResult<WorkTask>
            {
                Total = worktaskQuery.Count()
            };
            if (pageSize < 1)
                result.Items = worktaskQuery.Select(w => w.ToWorkTask()).ToList();
            else result.Items = worktaskQuery.OrderByDescending(w => w.CreationTime).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList().Select(w => w.ToWorkTask()).ToList();
            return await Task.FromResult(result);
        }


        private async Task<PageResult<StepWorkTask>> GetResultAsync(IQueryable<WorkStepInfo> workStepInfos, Expression<Func<WorkStepInfo, bool>> predicate, IQueryable<WorkTaskInfo> workTaskInfos, int pageIndex = 1, int pageSize = -1)
        {
            workTaskInfos = workTaskInfos.Where(wt => workStepInfos.Any(ws => ws.WorkTaskId == wt.Id));

            var count = workTaskInfos.Count();
            var tasks = workTaskInfos.OrderByDescending(wt => wt.CreationTime).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();

            var taskIds = tasks.Select(t => t.Id).ToList();
            var workStepInfosByTaskIds = workflowDbContext.Set<WorkStepInfo>().AsNoTracking()
                .WhereMultipleApp(appContextManager.GetAppId())
                .Where(ws => taskIds.Contains(ws.WorkTaskId))
                .Where(predicate)
                .ToList();

            workStepInfosByTaskIds = workStepInfosByTaskIds.OrderByDescending(ws => ws.CreationTime)
                .GroupBy(ws => ws.WorkTaskId)
                .Select(g => g.First())
                .ToList();

            return await Task.FromResult(new PageResult<StepWorkTask>()
            {
                Total = count,
                Items = tasks.Select(t => new StepWorkTask
                {
                    Step = workStepInfosByTaskIds.FirstOrDefault(ws => ws.WorkTaskId == t.Id)?.ToWorkStep(),
                    Task = tasks.FirstOrDefault(wt => wt.Id == t.Id)?.ToWorkTask(),
                }).ToList(),
            });
        }
    }
    public class StepWorkTaskQuery
    {
        public Guid TaskId { get; set; }
        public Guid StepId { get; set; }
        public DateTime CreationTIme { get; set; }
    }
}
